<!doctype html>




<html class="theme-next pisces" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>



<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />












  
  
    
  
  <link href="//cdn.staticfile.org/fancybox/2.1.5/jquery.fancybox.min.css" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  
    
      
    

    
  

  

  
    
      
    

    
  

  
    
      
    

    
  

  
    
    
    <link href="//fonts.lug.ustc.edu.cn/css?family=Monda:300,300italic,400,400italic,700,700italic|Roboto Slab:300,300italic,400,400italic,700,700italic|Lobster Two:300,300italic,400,400italic,700,700italic|PT Mono:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






  

<link href="//cdn.staticfile.org/font-awesome/4.6.2/css/font-awesome.min.css" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.0" rel="stylesheet" type="text/css" />


  <meta name="keywords" content="Web,JavaScript," />








  <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.0" />






<meta name="description" content="客户端检测面对普遍存在的不一致性问题，开发人员要么采取迁就各方的“最小公分母”策略，要么(也是更常见的)就得利用各种客户端检测方法，来突破或者规避种种局限性。迄今为止，客户端检测仍然是Web 开发领域中一个饱受争议的话题。在现实当中，浏览器之间的差异以及不同浏览器的“怪癖”(quirk)，多得简直不胜枚举。因此，客户端检测除了是一种补救措施之外，更是一种行之有效的开发策略。
但不到万不得已，就不要">
<meta property="og:type" content="article">
<meta property="og:title" content="《 JavaScript 高级程序设计》备忘-第九章-1">
<meta property="og:url" content="http://codingme.xyz/2016/09/30/javascript-note-09-1/index.html">
<meta property="og:site_name" content="CodingMe">
<meta property="og:description" content="客户端检测面对普遍存在的不一致性问题，开发人员要么采取迁就各方的“最小公分母”策略，要么(也是更常见的)就得利用各种客户端检测方法，来突破或者规避种种局限性。迄今为止，客户端检测仍然是Web 开发领域中一个饱受争议的话题。在现实当中，浏览器之间的差异以及不同浏览器的“怪癖”(quirk)，多得简直不胜枚举。因此，客户端检测除了是一种补救措施之外，更是一种行之有效的开发策略。
但不到万不得已，就不要">
<meta property="og:updated_time" content="2016-10-18T06:23:42.201Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="《 JavaScript 高级程序设计》备忘-第九章-1">
<meta name="twitter:description" content="客户端检测面对普遍存在的不一致性问题，开发人员要么采取迁就各方的“最小公分母”策略，要么(也是更常见的)就得利用各种客户端检测方法，来突破或者规避种种局限性。迄今为止，客户端检测仍然是Web 开发领域中一个饱受争议的话题。在现实当中，浏览器之间的差异以及不同浏览器的“怪癖”(quirk)，多得简直不胜枚举。因此，客户端检测除了是一种补救措施之外，更是一种行之有效的开发策略。
但不到万不得已，就不要">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Pisces',
    sidebar: {"position":"right","display":"post"},
    fancybox: true,
    motion: false,
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://codingme.xyz/2016/09/30/javascript-note-09-1/"/>





  <title> 《 JavaScript 高级程序设计》备忘-第九章-1 | CodingMe </title>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  










  
  
    
  

  <div class="container one-collumn sidebar-position-right page-post-detail ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-meta ">
  

  <div class="custom-logo-site-title">
    <a href="/"  class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <span class="site-title">CodingMe</span>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>
  <p class="site-subtitle"></p>
</div>

<div class="site-nav-toggle">
  <button>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
  </button>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/about" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />
            
            搜索
          </a>
        </li>
      
    </ul>
  

  
    <div class="site-search">
      
  <div class="popup">
 <span class="search-icon fa fa-search"></span>
 <input type="text" id="local-search-input">
 <div id="local-search-result"></div>
 <span class="popup-btn-close">close</span>
</div>


    </div>
  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
  <link itemprop="mainEntityOfPage" href="http://codingme.xyz/2016/09/30/javascript-note-09-1/">

  <span style="display:none" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <meta itemprop="name" content="Huang SJ">
    <meta itemprop="description" content="">
    <meta itemprop="image" content="/uploads/avatar.jpg">
  </span>

  <span style="display:none" itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
    <meta itemprop="name" content="CodingMe">
    <span style="display:none" itemprop="logo" itemscope itemtype="http://schema.org/ImageObject">
      <img style="display:none;" itemprop="url image" alt="CodingMe" src="">
    </span>
  </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
            
            
              
                《 JavaScript 高级程序设计》备忘-第九章-1
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">
            

            

            
          </span>

          

          
            
          

          

          
          

          

          

        </div>
      </header>
    


    <div class="post-body" itemprop="articleBody">

      
      

      
        <h1 id="客户端检测"><a href="#客户端检测" class="headerlink" title="客户端检测"></a>客户端检测</h1><p>面对普遍存在的不一致性问题，开发人员要么采取迁就各方的“最小公分母”策略，<br>要么(也是更常见的)就得利用各种客户端检测方法，来突破或者规避种种局限性。<br>迄今为止，客户端检测仍然是Web 开发领域中一个饱受争议的话题。<br>在现实当中，浏览器之间的差异以及不同浏览器的“怪癖”(quirk)，多得简直不胜枚举。<br>因此，客户端检测除了是一种补救措施之外，更是一种行之有效的开发策略。</p>
<p>但不到万不得已，就不要使用客户端检测。<br>只要能找到更通用的方法，就应该优先采用更通用的方法。<br>一言以蔽之，先设计最通用的方案，然后再使用特定于浏览器的技术增强该方案。</p>
<h1 id="能力检测"><a href="#能力检测" class="headerlink" title="能力检测"></a>能力检测</h1><p>(又称特性检测)。能力检测的目标不是识别特定的浏览器，而是识别浏览器的能力。<br>不必顾及特定的浏览器如何如何，只要确定浏览器支持特定的能力，就可以给出解决方案。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (object.propertyInQuestion)&#123;</div><div class="line">    <span class="comment">//使用object.propertyInQuestion</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>举例来说，IE5.0 之前的版本不支持document.getElementById()这个DOM 方法。<br>尽管可以使用非标准的document.all 属性实现相同的目的，<br>但IE 的早期版本中确实不存在document.getElementById()。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getElement</span>(<span class="params">id</span>)</span>&#123;</div><div class="line">    <span class="keyword">if</span> (<span class="built_in">document</span>.getElementById)&#123;</div><div class="line">        <span class="keyword">return</span> <span class="built_in">document</span>.getElementById(id);</div><div class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">document</span>.all)&#123;</div><div class="line">        <span class="keyword">return</span> <span class="built_in">document</span>.all[id];</div><div class="line">    &#125; <span class="keyword">else</span> &#123;</div><div class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">"No way to retrieve element!"</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<a id="more"></a>
<p>要理解能力检测，首先必须理解两个重要的概念。<br>第一个概念就是先检测达成目的的最常用的特性。先检测最常用的特性可以保证代码最优化，因为在多数情况下都可以避免测试多个条件。<br>第二个重要的概念就是必须测试实际要用到的特性。一个特性存在，不一定意味着另一个特性也存在。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getWindowWidth</span>(<span class="params"></span>)</span>&#123;</div><div class="line">    <span class="keyword">if</span> (<span class="built_in">document</span>.all)&#123; <span class="comment">//假设是IE</span></div><div class="line">        <span class="keyword">return</span> <span class="built_in">document</span>.documentElement.clientWidth; <span class="comment">//错误的用法！！！</span></div><div class="line">    &#125; <span class="keyword">else</span> &#123;</div><div class="line">        <span class="keyword">return</span> <span class="built_in">window</span>.innerWidth;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>document.all 存在也不一定表示浏览器就是IE。</p>
<h2 id="更可靠的能力检测"><a href="#更可靠的能力检测" class="headerlink" title="更可靠的能力检测"></a>更可靠的能力检测</h2><p>利用类型转换来确定某个对象成员是否存在，但这样你还是不知道该成员是不是你想要的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//不要这样做！这不是能力检测——只检测了是否存在相应的方法</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isSortable</span>(<span class="params">object</span>)</span>&#123;</div><div class="line">    <span class="keyword">return</span> !!object.sort;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这个函数通过检测对象是否存在sort()方法，来确定对象是否支持排序。<br>问题是，任何包含sort属性的对象也会返回true。<br><code>var result = isSortable({ sort: true });</code><br>检测某个属性是否存在并不能确定对象是否支持排序。更好的方式是检测sort 是不是一个函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//这样更好：检查sort 是不是函数</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isSortable</span>(<span class="params">object</span>)</span>&#123;</div><div class="line">    <span class="keyword">return</span> <span class="keyword">typeof</span> object.sort == <span class="string">"function"</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这里的typeof 操作符用于确定sort 的确是一个函数，因此可以调用它对数据进行排序。<br>在可能的情况下，要尽量使用typeof 进行能力检测。特别是，宿主对象没有义务让typeof 返回合理的值。</p>
<p>最令人发指的事儿就发生在IE 中。大多数浏览器在检测到document.createElement()存在时，都会返回true。<br>在IE8 及之前版本中，这个函数返回false。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">hasCreateElement</span>(<span class="params"></span>)</span>&#123;</div><div class="line">    <span class="keyword">return</span> <span class="keyword">typeof</span> <span class="built_in">document</span>.createElement == <span class="string">"function"</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>DOM对象是宿主对象，IE 及更早版本中的宿主对象是通过COM 而非JScript 实现的。<br>因此，document.createElement()函数确实是一个COM 对象，所以typeof 才会返回”object”。<br>IE9 纠正了这个问题，对所有DOM 方法都返回”function”。<br>关于typeof 的行为不标准，IE 中还可以举出例子来。ActiveX 对象(只有IE 支持)与其他对象的行<br>为差异很大。例如，不使用typeof 测试某个属性会导致错误。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//在IE 中会导致错误</span></div><div class="line"><span class="keyword">var</span> xhr = <span class="keyword">new</span> ActiveXObject(<span class="string">"Microsoft.XMLHttp"</span>);</div><div class="line"><span class="keyword">if</span> (xhr.open)&#123; <span class="comment">//这里会发生错误</span></div><div class="line">    <span class="comment">//执行操作</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>像这样直接把函数作为属性访问会导致JavaScript 错误。<br>使用typeof 操作符会更靠谱一点，但IE对typeof xhr.open 会返回”unknown”。<br>这就意味着，在浏览器环境下测试任何对象的某个特性是否存在，要使用下面这个函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//作者：Peter Michaux</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">isHostMethod</span>(<span class="params">object, property</span>) </span>&#123;</div><div class="line">    <span class="keyword">var</span> t = <span class="keyword">typeof</span> object[property];</div><div class="line">    <span class="keyword">return</span> t==<span class="string">'function'</span> ||</div><div class="line">    (!!(t==<span class="string">'object'</span> &amp;&amp; object[property])) ||</div><div class="line">    t==<span class="string">'unknown'</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line">result = isHostMethod(xhr, <span class="string">"open"</span>); <span class="comment">//true</span></div><div class="line">result = isHostMethod(xhr, <span class="string">"foo"</span>); <span class="comment">//false</span></div></pre></td></tr></table></figure>
<p>目前使用isHostMethod()方法还是比较可靠的，因为它考虑到了浏览器的怪异行为。<br>不过也要注意，宿主对象没有义务保持目前的实现方式不变，也不一定会模仿已有宿主对象的行为。</p>
<p>所以，这个函数——以及其他类似函数，都不能百分之百地保证永远可靠。<br><strong>作为开发人员，必须对自己要使用某个功能的风险作出理性的估计。</strong></p>
<h2 id="能力检测，不是浏览器检测"><a href="#能力检测，不是浏览器检测" class="headerlink" title="能力检测，不是浏览器检测"></a>能力检测，不是浏览器检测</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//错误！还不够具体</span></div><div class="line"><span class="keyword">var</span> isFirefox = !!(navigator.vendor &amp;&amp; navigator.vendorSub);</div><div class="line"><span class="comment">//错误！假设过头了</span></div><div class="line"><span class="keyword">var</span> isIE = !!(<span class="built_in">document</span>.all &amp;&amp; <span class="built_in">document</span>.uniqueID);</div></pre></td></tr></table></figure>
<p>这两行代码代表了对能力检测的典型误用。<br>以前，确实可以通过检测navigator.vendor 和navigator.vendorSub 来确定Firefox 浏览器。<br>但是，Safari 也依葫芦画瓢地实现了相同的属性。<br>于是，这段代码就会导致人们作出错误的判断。<br>为检测IE，代码测试了document.all 和document.uniqueID。<br>这就相当于假设IE 将来的版本中仍然会继续存在这两个属性，同时还假设其他浏览器都不会实现这两个属性。<br>最后，这两个检测都使用了双逻辑非操作符来得到布尔值(比先存储后访问的效果更好)。<br>实际上，根据浏览器不同将能力组合起来是更可取的方式。<br>如果你知道自己的应用程序需要使用某些特定的浏览器特性，那么最好是一次性检测所有相关特性，而不要分别检测。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//确定浏览器是否支持Netscape 风格的插件</span></div><div class="line"><span class="keyword">var</span> hasNSPlugins = !!(navigator.plugins &amp;&amp; navigator.plugins.length);</div><div class="line"><span class="comment">//确定浏览器是否具有DOM1 级规定的能力</span></div><div class="line"><span class="keyword">var</span> hasDOM1 = !!(<span class="built_in">document</span>.getElementById &amp;&amp; <span class="built_in">document</span>.createElement &amp;&amp;</div><div class="line"><span class="built_in">document</span>.getElementsByTagName);</div></pre></td></tr></table></figure>
<p>一个检测浏览器是否支持Netscapte 风格的插件；另一个检测浏览器是否具备DOM1 级所规定的能力。<br>得到的布尔值可以在以后继续使用，从而节省重新检测能力的时间。<br><strong>在实际开发中，应该将能力检测作为确定下一步解决方案的依据，而不是用它来判断用户使用的是什么浏览器。</strong></p>
<h1 id="怪癖检测"><a href="#怪癖检测" class="headerlink" title="怪癖检测"></a>怪癖检测</h1><p>怪癖检测(quirks detection)的目标是识别浏览器的特殊行为。<br>怪癖检测是想要知道浏览器存在什么缺陷(“怪癖”也就是bug)。<br>例如，IE8 及更早版本中存在一个bug，<br>即如果某个实例属性与[[Enumerable]]标记为false 的某个原型属性同名，<br>那么该实例属性将不会出现在fon-in 循环当中。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> hasDontEnumQuirk = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</div><div class="line">    <span class="keyword">var</span> o = &#123; toString : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125; &#125;;</div><div class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> prop <span class="keyword">in</span> o)&#123;</div><div class="line">        <span class="keyword">if</span> (prop == <span class="string">"toString"</span>)&#123;</div><div class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</div><div class="line">&#125;();</div></pre></td></tr></table></figure>
<p>通过一个匿名函数来测试该“怪癖”，函数中创建了一个带有toString()方法的对象。<br>在正确的ECMAScript 实现中，toString 应该在for-in 循环中作为属性返回。</p>
<h1 id="用户代理检测"><a href="#用户代理检测" class="headerlink" title="用户代理检测"></a>用户代理检测</h1><p>争议最大的一种客户端检测技术。<br>用户代理检测通过检测用户代理字符串来确定实际使用的浏览器。<br>用户代理字符串是作为响应首部发送的，而且该字符串可以通过JavaScript 的navigator.userAgent 属性访问。<br>在服务器端，通过检测用户代理字符串来确定用户使用的浏览器是一种常用而且广为接受的做法。<br>而在客户端，用户代理检测一般被当作一种万不得已才用的做法，其优先级排在能力检测和(或)怪癖检测之后。</p>
<p>争议：电子欺骗(spoofing)。所谓电子欺骗，就是指浏览器通过在自己的用户代理字符串加入一些错误或误导性信息，来达到欺骗服务器的目的。</p>
<h2 id="用户代理字符串的历史"><a href="#用户代理字符串的历史" class="headerlink" title="用户代理字符串的历史"></a>用户代理字符串的历史</h2><p>HTTP 规范(包括1.0 和1.1 版)明确规定，浏览器应该发送简短的用户代理字符串，指明浏览器的名称和版本号。<br>描述：“产品标识符常用于通信应用程序标识自身，由软件名和版本组成。<br>使用产品标识符的大多数领域也允许列出作为应用程序主要部分的子产品，由空格分隔。<br>按照惯例，产品要按照相应的重要程度依次列出，以便标识应用程序。”</p>
<ol>
<li><strong>早期的浏览器</strong><br> 美国NCSA的世界上第一款Web 浏览器Mosaic。它的用户代理字符串:<code>Mosaic/0.9</code><br> 后面网景公司开发的Netscapse Navigator2 具有的用户代理字符串：<code>Mozilla/版本号 [语言] (平台; 加密类型)</code><br> Netscape 在坚持将产品名和版本号作为用户代理字符串开头的基础上，又在后面依次添加了<br> 语言、<br> 平台、<br> 加密类型,可能的值有U(128 位加密)、I(40 位加密)和N(未加密)。<br> 典型的Netscape Navigator 2 的用户代理字符串：<code>Mozilla/2.02 [fr] (WinNT; I)</code><br> 浏览器是Netscape Navigator 2.02，为法语国家编译，运行在Windows NT 平台下，加密类型为40 位。</li>
<li><strong>Netscape Navigator 3 和Internet Explorer 3</strong><br> 1996 年，Netscape Navigator 3 发布，格式变成<code>Mozilla/版本号 (平台; 加密类型 [; 操作系统或CPU 说明])</code><br> Windows 系统下的用户代理字符串：<code>Mozilla/3.0 (Win95; U)</code><br> 之后微软发布 IE3,用户代理字符串决定兼容 Netscape 形式:<code>Mozilla/2.0 (compatible; MSIE 版本号; 操作系统)</code><br> 如<code>Mozilla/2.0 (compatible; MSIE 3.02; Windows 95)</code><br> 于是 IE 成功将自己标识成 Mozilla,伪装成 Netscape Navigator。因为违反了浏览器标识惯例招致许多批评。<br> 另外一个有趣的地方是标识符Mozilla 2.0(而不是3.0)。毕竟，当时的主流版本是3.0，改成3.0 应该对微软更有利才对。<br> 但真正的谜底到现在还没有揭开——但很可能只是人为疏忽所致。</li>
<li><strong>Netscape Communicator 4 和IE4～IE8</strong><br> 1997 年8 月，Netscapte Communicator 4 发布。<code>Mozilla/4.0 (Win98; I)</code><br> 发布补丁时，子版本号也会相应提高<code>Mozilla/4.79 (Win98; I)</code><br> 微软在发布Internet Explorer 4 时，顺便将用户代理字符串修改<code>Mozilla/4.0 (compatible; MSIE 4.0; Windows 98)</code><br> 两者的一致性仅限于这一个版本。<br> 此后，IE 的版本一直到7<code>Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)</code><br> 而IE8 的用户代理字符串中添加了呈现[Render]引擎(Trident)的版本号：<code>Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)</code><br> 这个新增的Trident 记号是为了让开发人员知道IE8 是不是在兼容模式下运行。<br> 如果是，则MSIE 的版本号会变成7，但Trident 及版本号还会留在用户代码字符串中。<br> IE9 对字符串格式做了一点调整。Mozilla 版本号增加到了5.0，而Trident 的版本号也升到了5.0。<br> <code>Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)</code><br> 如果IE9 运行在兼容模式下，字符串中的Mozilla 版本号和MSIE 版本号会恢复旧的值，但Trident的版本号仍然是5.0。<br> <code>Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0)</code></li>
<li><strong>Gecko</strong><br> Gecko 是Firefox 的呈现[Render]引擎。<br> 当初的是作为通用Mozilla 浏览器的一部分开发的。<br> 第一个采用Gecko 引擎的浏览器是Netscape 6。<br> Netscape 6改的用户代理字符串，加星号的非必需。<pre><code>Mozilla/Mozilla 版本号 (平台; 加密类型; 操作系统或CPU; 语言; *预先发行版本)
Gecko/*Gecko 版本号 *应用程序或产品/ *应用程序或产品版本号
</code></pre> 例子<pre><code>Windows XP 下的Netscape 6.21：
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:0.9.4) Gecko/20011128 Netscape6/6.2.1
Linux 下的SeaMonkey 1.1a：
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1b2) Gecko/20060823 SeaMonkey/1.1a
Windows XP 下的Firefox 2.0.0.11：
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11
Mac OS X 下的Camino 1.5.1：
Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en; rv:1.8.1.6) Gecko/20070809 Camino/1.5.1
</code></pre> 之后的 Mozilla Firefox 4 发布，用户代理字符串变成：<code>Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox 4.0.1</code></li>
<li><strong>WebKit</strong><br> 2003 年，Apple 公司宣布要发布自己的Web 浏览器，名字定为Safari。<br> Safari 的呈现[Render]引擎叫WebKit，是Linux 平台中 Konqueror 浏览器的呈现[Render]引擎KHTML 的一个分支。<br> 之后WebKit 独立出来成为了一个开源项目，专注于呈现[Render]引擎的开发。<br> 这款新浏览器和呈现引擎的开发人员也遇到了与Internet Explorer 3.0 类似的问题：<br> 如何确保这款浏览器不被流行的站点拒之门外？<br> 向用户代理字符串中放入足够多的信息，以便站点能够信任它与其他流行的浏览器是兼容的。<br> 于是 WebKit 的用户代理字符串就变成了:<pre><code>Mozilla/5.0 (平台; 加密类型; 操作系统或CPU; 语言) AppleWebKit/AppleWebKit 版本号
(KHTML, like Gecko) Safari/Safari 版本号
</code></pre> 实例:<pre><code>Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/124 (KHTML, like Gecko)
Safari/125.1
</code></pre> 出于兼容性的考虑，有关人员很快就决定了将Safari 标识为Mozilla。<br> 至今，基于WebKit 的所有浏览器都将自己标识为Mozilla 5.0，与基于Gecko 的浏览器完全一样。<br> Safari 3.0 发布时<pre><code>Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/522.15.5 (KHTML, like
Gecko) Version/3.0.3 Safari/522.15.5
</code></pre></li>
<li><strong>Konqueror</strong><br> 与KDE Linux 集成的Konqueror，是一款基于KHTML 开源呈现引擎的浏览器。<br> <code>Mozilla/5.0 (compatible; Konqueror/ 版本号; 操作系统或CPU )</code><br> 到了3.2版本，将自己标识为KHTML<br> <code>Mozilla/5.0 (compatible; Konqueror/ 版本号; 操作系统或CPU) KHTML/ KHTML 版本号 (like Gecko)</code><br> 例子：<br> <code>Mozilla/5.0 (compatible; Konqueror/3.5; SunOS) KHTML/3.5.0 (like Gecko)</code><br> 其中 Konqueror 与 KHTML 版本号比较一致，即使有差别也比较小(不像 Safari )</li>
<li><strong>Chrome</strong><br> 谷歌公司的Chrome 浏览器以WebKit 作为呈现[Render]引擎，但使用了不同的JavaScript 引擎。<br> Chrome 0.2的用户代理字符串:<pre><code>Mozilla/5.0 ( 平台; 加密类型; 操作系统或CPU; 语言) AppleWebKit/AppleWebKit 版本号 (KHTML,
like Gecko) Chrome/ Chrome 版本号 Safari/ Safari 版本
</code></pre> Chrome 7的完整用户代理字符串：<pre><code>Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.7 (KHTML,
like Gecko) Chrome/7.0.517.44 Safari/534.7
</code></pre> 现在windows10 x64 系统下的 Chrome 53 x64的完整用户代理字符串：<br> <code>Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36</code></li>
<li>opera<br> 已经完全没有市场份额，GG。</li>
<li><p>iOS 和Android<br> 移动操作系统iOS 和 Android 默认的浏览器都基于WebKit，而且都像它们的桌面版一样，共享相同的基本用户代理字符串格式。<br> iOS:</p>
<pre><code>Mozilla/5.0 (平台; 加密类型; 操作系统或CPU like Mac OS X; 语言)
AppleWebKit/AppleWebKit 版本号 (KHTML, like Gecko) Version/浏览器版本号
Mobile/移动版本号 Safari/Safari 版本号
</code></pre><p> 例子：</p>
<pre><code>Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)
AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16
</code></pre><p> Android:</p>
<pre><code>Mozilla/5.0 (Linux; U; Android 2.2; en-us; Nexus One Build/FRF91)
AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
</code></pre></li>
</ol>

      
    </div>

    <div>
      
        

      
    </div>

    <div>
      
        

      
    </div>


    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/Web/" rel="tag"># Web</a>
          
            <a href="/tags/JavaScript/" rel="tag"># JavaScript</a>
          
        </div>
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2016/09/29/javascript-note-08-1/" rel="next" title="《 JavaScript 高级程序设计》备忘-第八章-1">
                <i class="fa fa-chevron-left"></i> 《 JavaScript 高级程序设计》备忘-第八章-1
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2016/09/30/javascript-note-08-2/" rel="prev" title="《 JavaScript 高级程序设计》备忘-第八章-2">
                《 JavaScript 高级程序设计》备忘-第八章-2 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          
  <div class="comments" id="comments">
    
  </div>


        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <img class="site-author-image" itemprop="image"
               src="/uploads/avatar.jpg"
               alt="Huang SJ" />
          <p class="site-author-name" itemprop="name">Huang SJ</p>
          <p class="site-description motion-element" itemprop="description"></p>
        </div>
        <nav class="site-state motion-element">
          <div class="site-state-item site-state-posts">
            <a href="/archives">
              <span class="site-state-item-count">75</span>
              <span class="site-state-item-name">日志</span>
            </a>
          </div>

          
            <div class="site-state-item site-state-categories">
              <a href="/categories">
                <span class="site-state-item-count">4</span>
                <span class="site-state-item-name">分类</span>
              </a>
            </div>
          

          
            <div class="site-state-item site-state-tags">
              <a href="/tags">
                <span class="site-state-item-count">9</span>
                <span class="site-state-item-name">标签</span>
              </a>
            </div>
          

        </nav>

        

        <div class="links-of-author motion-element">
          
            
              <span class="links-of-author-item">
                <a href="https://github.com/hsj1992" target="_blank" title="GitHub">
                  
                    <i class="fa fa-fw fa-github"></i>
                  
                  GitHub
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="https://twitter.com/hsj9527" target="_blank" title="Twitter">
                  
                    <i class="fa fa-fw fa-twitter"></i>
                  
                  Twitter
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="http://weibo.com/u/1694147905" target="_blank" title="Weibo">
                  
                    <i class="fa fa-fw fa-weibo"></i>
                  
                  Weibo
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="https://www.freecodecamp.cn/hsj1992" target="_blank" title="FreeCodeCamp">
                  
                    <i class="fa fa-fw fa-fire"></i>
                  
                  FreeCodeCamp
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="http://codepen.io/hsj1992/" target="_blank" title="codepen">
                  
                    <i class="fa fa-fw fa-codepen"></i>
                  
                  codepen
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="http://hsj1992.oschina.io/" target="_blank" title="码云镜像">
                  
                    <i class="fa fa-fw fa-code"></i>
                  
                  码云镜像
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="https://hsj1992.coding.me/" target="_blank" title="coding镜像">
                  
                    <i class="fa fa-fw fa-code"></i>
                  
                  coding镜像
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="https://hsj1992.github.io/" target="_blank" title="github镜像">
                  
                    <i class="fa fa-fw fa-code"></i>
                  
                  github镜像
                </a>
              </span>
            
              <span class="links-of-author-item">
                <a href="https://codingme-6dd8a.firebaseapp.com" target="_blank" title="Firebase镜像">
                  
                    <i class="fa fa-fw fa-google"></i>
                  
                  Firebase镜像
                </a>
              </span>
            
          
        </div>

        
        

        
        

        


      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#客户端检测"><span class="nav-number">1.</span> <span class="nav-text">客户端检测</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#能力检测"><span class="nav-number">2.</span> <span class="nav-text">能力检测</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#更可靠的能力检测"><span class="nav-number">2.1.</span> <span class="nav-text">更可靠的能力检测</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#能力检测，不是浏览器检测"><span class="nav-number">2.2.</span> <span class="nav-text">能力检测，不是浏览器检测</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#怪癖检测"><span class="nav-number">3.</span> <span class="nav-text">怪癖检测</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#用户代理检测"><span class="nav-number">4.</span> <span class="nav-text">用户代理检测</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#用户代理字符串的历史"><span class="nav-number">4.1.</span> <span class="nav-text">用户代理字符串的历史</span></a></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  
  &copy;  2015 - 
  <span itemprop="copyrightYear">2016</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Huang SJ</span>
</div>


<div class="powered-by">
  由 <a class="theme-link" href="https://hexo.io">Hexo</a> 强力驱动
</div>

<div class="theme-info">
  主题 -
  <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">
    NexT.Pisces
  </a>
</div>


        

        
      </div>
    </footer>

    <div class="back-to-top">
      <i class="fa fa-arrow-up"></i>
    </div>
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  



  
  <script type="text/javascript" src="//cdn.staticfile.org/jquery/2.1.3/jquery.min.js"></script>

  
  <script type="text/javascript" src="//cdn.staticfile.org/fastclick/1.0.6/fastclick.min.js"></script>

  
  <script type="text/javascript" src="//cdn.staticfile.org/jquery_lazyload/1.9.7/jquery.lazyload.min.js"></script>

  
  <script type="text/javascript" src="//cdn.staticfile.org/velocity/1.2.1/velocity.min.js"></script>

  
  <script type="text/javascript" src="//cdn.staticfile.org/velocity/1.2.1/velocity.ui.min.js"></script>

  
  <script type="text/javascript" src="//cdn.staticfile.org/fancybox/2.1.5/jquery.fancybox.pack.js"></script>


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.0"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.0"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.0"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.0"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.0"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.0"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.0"></script>



  



  




	




  
  
  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length == 0) {
      search_path = "search.xml";
    }
    var path = "/" + search_path;
    // monitor main search box;

    function proceedsearch() {
      $("body").append('<div class="popoverlay">').css('overflow', 'hidden');
      $('.popup').toggle();
    }
    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';
      $.ajax({
        url: path,
        dataType: "xml",
        async: true,
        success: function( xmlResponse ) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = $( "entry", xmlResponse ).map(function() {
            return {
              title: $( "title", this ).text(),
              content: $("content",this).text(),
              url: $( "url" , this).text()
            };
          }).get();
          var $input = document.getElementById(search_id);
          var $resultContent = document.getElementById(content_id);
          $input.addEventListener('input', function(){
            var matchcounts = 0;
            var str='<ul class=\"search-result-list\">';
            var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
            $resultContent.innerHTML = "";
            if (this.value.trim().length > 1) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var content_index = [];
                var data_title = data.title.trim().toLowerCase();
                var data_content = data.content.trim().replace(/<[^>]+>/g,"").toLowerCase();
                var data_url = decodeURIComponent(data.url);
                var index_title = -1;
                var index_content = -1;
                var first_occur = -1;
                // only match artiles with not empty titles and contents
                if(data_title != '') {
                  keywords.forEach(function(keyword, i) {
                    index_title = data_title.indexOf(keyword);
                    index_content = data_content.indexOf(keyword);
                    if( index_title >= 0 || index_content >= 0 ){
                      isMatch = true;
                      if (i == 0) {
                        first_occur = index_content;
                      }
                    }

                  });
                }
                // show search results
                if (isMatch) {
                  matchcounts += 1;
                  str += "<li><a href='"+ data_url +"' class='search-result-title'>"+ data_title +"</a>";
                  var content = data.content.trim().replace(/<[^>]+>/g,"");
                  if (first_occur >= 0) {
                    // cut out 100 characters
                    var start = first_occur - 20;
                    var end = first_occur + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if(start == 0){
                      end = 50;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    var match_content = content.substring(start, end);
                    // highlight all keywords
                    keywords.forEach(function(keyword){
                      var regS = new RegExp(keyword, "gi");
                      match_content = match_content.replace(regS, "<b class=\"search-keyword\">"+keyword+"</b>");
                    });

                    str += "<p class=\"search-result\">" + match_content +"...</p>"
                  }
                  str += "</li>";
                }
              })};
            str += "</ul>";
            if (matchcounts == 0) { str = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>' }
            if (keywords == "") { str = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>' }
            $resultContent.innerHTML = str;
          });
          proceedsearch();
        }
      });}

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched == false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(function(e){
      $('.popup').hide();
      $(".popoverlay").remove();
      $('body').css('overflow', '');
    });
    $('.popup').click(function(e){
      e.stopPropagation();
    });
  </script>


  

  

  

  


</body>
</html>
